/**
 * Created with JetBrains WebStorm.
 * User: Eric
 * Date: 19/03/13
 * Time: 16:33
 * To change this template use File | Settings | File Templates.
 */
// We create a connection
var connector = require('./connector');
var connection = connector.connection();
// We need our model
var Tile = require("./Tile.js");

// a example of method to retrieve an user or a list of user from the db
var findByCoordinates = function (cx, cy, callback){ // we need a callback because the query is asynchronous


    connection.config.queryFormat = function (query, values) {
        if (!values) return query;
        return query.replace(/\:(\w+)/g, function (txt, key) {
            if (values.hasOwnProperty(key)) {
                return this.escape(values[key]);
            }
            return txt;
        }.bind(this));
    };

    console.log(cx);
    var tiles = new Array(); // Our model that we can instanciate
    var options = {sql: 'SELECT * from tile t where t.coordinateX >= :minX and t.coordinateX <= :maxX and t.coordinateY >= :minY and t.coordinateY <= :maxY',
        values : {minX : cx.min, maxX : cx.max, minY : cy.min, maxY : cy.max}}

    connection.query(options, function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        for(var i = 0; i < rows.length; i++){

            var t = new Tile();
            t.init(rows[i]);
            tiles.push(t);
        }

        callback(tiles); // Callback basicaly it's the end of the controleur
    });

};


var givePersonage = function (x,y,personageId, callback){

    connection.config.queryFormat = function (query, values) {
        if (!values) return query;
        return query.replace(/\:(\w+)/g, function (txt, key) {
            if (values.hasOwnProperty(key)) {
                return this.escape(values[key]);
            }
            return txt;
        }.bind(this));
    };
    console.log("init p " + personageId);
    var options = {sql: 'Update tile set personage = :id where ' +
        'coordinateX > :minX and coordinateX < :maxX and coordinateY > :minY and coordinateY < :maxY',
        values : {id : personageId, minX : x - global.GameRules.GrantedTileRadiusEasy, maxX : x + global.GameRules.GrantedTileRadiusEasy -
                                            global.GameRules.TileSize,
            minY : y - global.GameRules.GrantedTileRadiusEasy +
                global.GameRules.TileSize, maxY : y + global.GameRules.GrantedTileRadiusEasy  } }
    connection.query(options, function(err, rows, fields) { // we do the query
        callback();
    });
}

var getFirstFreeTile = function(radius, callback){
    var t = null;
    connection.config.queryFormat = function (query, values) {
        if (!values) return query;
        return query.replace(/\:(\w+)/g, function (txt, key) {
            if (values.hasOwnProperty(key)) {
                return this.escape(values[key]);
            }
            return txt;
        }.bind(this));
    };


    var av = (global.GameRules.GrantedTileRadiusEasy*2) / global.GameRules.TileSize;
    av = av * av;

    console.log("av + " +av);
    var options = {
        sql: 'SELECT t.* FROM  tile t WHERE not exists (select * from tile tp where tp.personage is not null '+
            'and (t.coordinateX > (tp.coordinateX - :radius) '+
            'and t.coordinateX < (tp.coordinateX + :radius)) and (t.coordinateY > '+
            '( tp.coordinateY - :radius) and t.coordinateY < (tp.coordinateY + :radius)) ) '+
            'and :av <= (select count(*) from tile tr where tr.personage is null and '+
            '(tr.coordinateX >= (t.coordinateX - :avRadius) and tr.coordinateX <= (t.coordinateX + :avRadius)) and '+
            '(tr.coordinateY >= (t.coordinateY - :avRadius) and tr.coordinateY <= (t.coordinateY + :avRadius))) limit 1',
        values : {radius : global.GameRules.PlayerDistance, av: av , avRadius : global.GameRules.GrantedTileRadiusEasy }}

    connection.query(options, function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        for(var i = 0; i < rows.length; i++){

            t = new Tile();
            t.init(rows[i]);
        }

        callback(t); // Callback basicaly it's the end of the controleur
    });
}


var getCenterTile= function(callback){
    var t;

    var options = {sql: 'SELECT * from tile t where t.coordinateX = 300 and t.coordinateY = 300'}

    connection.query(options, function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        for(var i = 0; i < rows.length; i++){

            t = new Tile();
            t.init(rows[i]);
        }

        callback(t); // Callback basicaly it's the end of the controleur
    });
}

var expandWorld = function (x,y, width, height,xDirection,yDirection,callback){
    var bandLength = global.GameRules.FertilityBandLength;
    var ts = new Array();
    var dimentions = {width : global.GameRules.TileSize, height : global.GameRules.TileSize};
    var xb = x;
    var yb = y;
    var band = 0;
    var base = 0;
    for(var tx = xb, x = xb; x < xb + width; x += dimentions.width, tx += xDirection * dimentions.width ){
        band = 0;
        base = 0;
        for(var ty = yb, y = yb; y < yb + height; y += dimentions.height, ty += yDirection * dimentions.height){
            if (band++ % bandLength == 0){
                base = getRandomFertility();
            }
            //console.log("land : "+ band +  ", " + bandLength + ", "+band % bandLength);
            var t = new Tile();
            var fert = base + Math.floor((Math.random()*10));
            t.maxFertility = fert;
            t.fertility = fert;
            t.humidity = 0;
            t.width = dimentions.width;
            t.height = dimentions.height;
            t.coordinateX = tx;
            t.coordinateY = ty;
            t.health = null;
            t.personage = null;
            ts.push(t);
        }
    }

    addAll(ts,callback);

}

var getRandomFertility = function(){
    var r = Math.floor((Math.random()*10)*10);
    if (r >= 100)
        r = 90;
    return r;

}


var expandWorldImpl = function(callback){
    getBoundary(function(tiles){
        if (tiles["ur"] == undefined){ // no tile in database
            expandWorld(0,0,global.GameRules.ExpandWorldSize,global.GameRules.ExpandWorldSize,1,1,function(){ // expand the world from 0 0
                getCenterTile(function(tile){ // we return the center of the world
                    callback(tile);
                });
            });
        }else{ // there are tiles in database


            // we calc the length of boudaries
            var uw = tiles["ur"].coordinateX - tiles["ul"].coordinateX;
            var dw = tiles["dr"].coordinateX - tiles["dl"].coordinateX;
            var lh = tiles["lu"].coordinateY - tiles["ld"].coordinateY;
            var rh = tiles["ru"].coordinateY - tiles["rd"].coordinateY;


            console.log(uw+";"+dw+";"+lh+";"+rh);

            if (uw == dw && lh == rh && lh == uw){ // it's a square
                console.log("square");
                // we expand from upper left (RIGHT UP)
                console.log(tiles["ul"]);
                expandWorld(tiles["ur"].coordinateX,tiles["ur"].coordinateY + global.GameRules.TileSize,
                    global.GameRules.ExpandWorldSize,
                    global.GameRules.ExpandWorldSize,1,1,function(){
                        getNewPosition(callback);
                    });
                return;
            }else{ //it's not a square
                if (uw == dw){ // is the base equal to the top length
                    console.log("uw = dw");
                    if (lh == rh){ // is it a rectangle
                        console.log("rectangle");
                        if (uw > lh){ // more large than long
                            console.log("plus large que grand");
                            // We expand from upper left (Right UP)2
                            expandWorld(tiles["ul"].coordinateX,tiles["ul"].coordinateY + global.GameRules.TileSize,
                                global.GameRules.ExpandWorldSize,
                                global.GameRules.ExpandWorldSize,1,1,function(){
                                    getNewPosition(callback);
                                });
                            return;
                        }else{ // more long than large
                            console.log("plus grand que large");
                            // we expand from upper right (RIGHT DOWN)
                            expandWorld(tiles["ur"].coordinateX + global.GameRules.TileSize,tiles["ur"].coordinateY,
                                global.GameRules.ExpandWorldSize,
                                global.GameRules.ExpandWorldSize,1,-1,function(){
                                    getNewPosition(callback);
                                });
                            return;
                        }
                    }else{
                        console.log("case not  bibu implemented");
                    }
                }else{
                    if (lh == rh){ // cannot be with the map construction
                        console.log("case not implemented");
                    }else{
                        if (tiles["ur"].coordinateX == tiles["dr"].coordinateX){ // top right X aligned with bottom right X
                            if (tiles["ul"].coordinateX > tiles["dl"].coordinateX){ //
                                console.log("RD -> right");
                                expandWorld(tiles["ld"].coordinateX,tiles["ld"].coordinateY - global.GameRules.TileSize,
                                    global.GameRules.ExpandWorldSize,
                                    global.GameRules.ExpandWorldSize,1,-1,function(){
                                        getNewPosition(callback);
                                    });
                                return;
                            }else{
                                console.log("UL -> left");
                                expandWorld(tiles["ul"].coordinateX-50,tiles["ul"].coordinateY - global.GameRules.TileSize,
                                    global.GameRules.ExpandWorldSize,
                                    global.GameRules.ExpandWorldSize,-1,-1,function(){
                                        getNewPosition(callback);
                                    });
                                return;
                            }
                        }else{
                            if (tiles["ul"].coordinateX == tiles["dl"].coordinateX){
                                if (tiles["ur"].coordinateX > tiles["dr"].coordinateX){
                                    console.log("RD -> right");
                                    expandWorld(tiles["rd"].coordinateX - global.GameRules.TileSize,tiles["rd"].coordinateY,
                                        global.GameRules.ExpandWorldSize,
                                        global.GameRules.ExpandWorldSize,-1,-1,function(){
                                            getNewPosition(callback);
                                        });
                                    return;
                                }else{
                                    console.log("UL -> left");
                                    expandWorld(tiles["ur"].coordinateX,tiles["ur"].coordinateY,
                                        global.GameRules.ExpandWorldSize,
                                        global.GameRules.ExpandWorldSize,1,-1,function(){
                                            getNewPosition(callback);
                                        });
                                    return;
                                }
                                console.log("RD -> left");
                            }else{
                                console.log("case not implemented");
                            }

                        }
                    }
                }
            }
        }
    })
}
var getBoundary = function (callback){
    var tiles = new Array();
    getExtreme("coordinateX","max","coordinateY","asc",function(tile){
        tiles["rd"] = tile;
        getExtreme("coordinateX","max","coordinateY","desc", function(tile){
            tiles["ru"] = tile;
            getExtreme("coordinateX","min","coordinateY","asc", function(tile){
                tiles["ld"] = tile;
                getExtreme("coordinateX","min","coordinateY","desc", function(tile){
                    tiles["lu"] = tile;
                    getExtreme("coordinateY","max","coordinateX","asc", function(tile){
                        tiles["ul"] = tile;
                        getExtreme("coordinateY","max","coordinateX","desc", function(tile){
                            tiles["ur"] = tile;
                            getExtreme("coordinateY","min","coordinateX","asc", function(tile){
                                tiles["dl"] = tile;
                                getExtreme("coordinateY","min","coordinateX","desc", function(tile){
                                    tiles["dr"] = tile;
                                    //console.log(tiles);
                                    callback(tiles);
                                });
                            });
                        });
                    });
                });
            });
        });
    });

}


var getExtreme = function(from, extreme,order, direction, callback){

    var t;

    var options = {sql: 'SELECT * from tile t where t.'+from+' = (select '+extreme+'(t2.'+from+') from tile t2) order by '
        + order +' ' + direction + ' limit 1'}

    connection.query(options, function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        for(var i = 0; i < rows.length; i++){

            t = new Tile();
            t.init(rows[i]);
        }

        callback(t); // Callback basicaly it's the end of the controleur
    });


}
/*
find Upper Left
 SELECT * FROM `tile` WHERE coordinateX = (select max(t.coordinateX) from tile t) order by coordinateY asc limit 1
 SELECT * FROM `tile` WHERE coordinateX = (select max(t.coordinateX) from tile t) order by coordinateY desc limit 1
 */

var findTiles = function (callback){ // we need a callback because the query is asynchronous

    var tile = new tile(); // Our model that we can instanciate
    connection.query('SELECT * from tile', function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        if (rows.length === 1){ // there is one result
            //process.stdout.write("personage found : " + email );
            tile.init(rows[0]); // we init the model with the row
        }else{
            process.stdout.write("tile not found : " + tile.id);
            tile = null; // We didin't find the personage
        }
        callback(tile); // Callback basicaly it's the end of the controleur
    });

};


var findTileById = function (id,callback){ // we need a callback because the query is asynchronous

    var id = id.id;
    console.log(id);
    var tile = new Tile(); // Our model that we can instanciate
    connection.query('SELECT * from tile t where t.id = '+id, function(err, rows, fields) { // we do the query
        if (err) throw err; // if there is an error we throw it
        if (rows.length === 1){ // there is one result
            //process.stdout.write("personage found : " + email );
            tile.init(rows[0]); // we init the model with the row

        }else{
            process.stdout.write("tile not found : " + tile.id);
            tile = null; // We didn't find the personage
        }
        callback(tile); // Callback basicaly it's the end of the controleur
    });

};





var addAll= function(ts, callback){
    var values = new Array();
    for( var i = 0; i < ts.length; i++){
        var a = [ts[i].width, ts[i].height, ts[i].coordinateX, ts[i].coordinateY, 0,null, null,
            ts[i].fertility,ts[i].maxFertility,ts[i].humidity];
        values.push(a);
    }
    connection.config.queryFormat = function (query, values) {
        if (!values) return query;
        return query.replace(/\:(\w+)/g, function (txt, key) {
            if (values.hasOwnProperty(key)) {
                return this.escape(values[key]);
            }
            return txt;
        }.bind(this));
    };
    var options = {sql: 'INSERT INTO tile (width, height, coordinateX, coordinateY, coordinateZ, ' +
        'personage, plantation,fertility,maxFertility,humidity) VALUES :val',
        values : {val : values} }
    //console.log(values);
    connection.query(options, function(err, rows, fields) { // we do the query
        callback();
    });
}

var persistTile= function(tile, callback){

    console.log("on est dans persists tile");

    var post  = {coordinateX: tile.coordinateX, coordinateY: tile.coordinateY, coordinateZ: tile.coordinateZ, width: tile.width, height: tile.height,
        personage: tile.getPersonageForPersist(), humidity : tile.humidity, fertility : tile.fertility,
                maxFertility : tile.maxFertility};

    console.log(tile);
    if (tile.id == null){
        connection.query('INSERT INTO tile SET ?', post, function(err, rows, fields){

            if (err) throw err; // if there is an error we throw it
            callback(tile); // Callback basicaly it's the end of the controleur
        });
    }else{

        connection.query('update tile SET fertility = :f, maxFertility = :mf, humidity = :h where id = :id',{ f: tile.fertility,
                        mf : tile.maxFertility, h : tile.humidity, id : tile.id}, function(err, rows, fields){



            if (err) throw err; // if there is an error we throw it
            if (callback != null)
                callback(tile); // Callback basicaly it's the end of the controleur
        });
    }


};




var getNewPosition = function(callback){
    getFirstFreeTile(1000,function(tile){
        if(tile != null || tile != undefined){
            callback(tile);
        }else{
            console.log(tile);
            console.log("not found");
            expandWorldImpl(callback);
        }
    });

}







module.exports.findTileById = findTileById;
module.exports.findByCoordinates = findByCoordinates;
module.exports.findTiles = findTiles;
module.exports.persistTile = persistTile;
module.exports.addAll = addAll;
module.exports.getNewPosition = getNewPosition;
module.exports.initTerritory = givePersonage;
